package ru.aslanov.schedule.server.gcalendar;

import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.batch.BatchOperationType;
import com.google.gdata.data.batch.BatchUtils;
import com.google.gdata.data.calendar.CalendarEventEntry;
import com.google.gdata.data.calendar.CalendarEventFeed;
import com.google.gdata.data.calendar.CalendarFeed;
import com.google.gdata.util.RedirectRequiredException;
import com.google.gdata.util.ServiceException;
import org.w3c.dom.Document;
import ru.aslanov.schedule.model.GoogleCalendarSync;
import ru.aslanov.schedule.model.Schedule;
import ru.aslanov.schedule.server.PMF;
import ru.aslanov.schedule.utils.XMLUtil;

import javax.jdo.PersistenceManager;
import javax.xml.parsers.DocumentBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;
import java.util.logging.Logger;

/**
 * Created by IntelliJ IDEA.
 * Created: Mar 4, 2010 4:49:58 PM
 *
 * @author Sergey Aslanov
 */
public class GCalendarConnectService {
    private static final Logger log = Logger.getLogger(GCalendarConnectService.class.getName());
    private static final int MAX_BATCH_SIZE = 10;
    
    private CalendarService calendarService;
        
    private CalendarEventFeed batchFeed;
    private int batchDelete;
    private int batchInsert;
    private URL batchUrl;

    private BatchResultProcessor batchResultProcessor;


    public GCalendarConnectService(String scheduleKey) throws Exception {
        final PersistenceManager pm = PMF.getThreadLocalPersistenceManager();

        //try {
            Schedule schedule = pm.getObjectById(Schedule.class, scheduleKey);
            final GoogleCalendarSync googleCalendarSync = schedule.getGoogleCalendarSync();

            String authSubToken = googleCalendarSync != null ? googleCalendarSync.getSessionToken() : null;
            if (authSubToken == null) {
                throw new Exception("You are not authorized in Google Calendar");
            }

            calendarService = new CalendarService("schedule-manager");
            calendarService.setAuthSubToken(authSubToken);
            calendarService.setConnectTimeout(30000);
            calendarService.setReadTimeout(30000);
        //} finally {
        //    pm.close();
        //}
    }

    public CalendarEventFeed getEventsFeed(String calId) throws IOException, ServiceException {
        final URL eventsURL = getCalendarEventsURL(calId, true);
        log.info("Loading existing events for calendar: '" + calId + "', feed url: " + eventsURL);
        CalendarEventFeed calendarEventFeed = null;
        try {
            calendarEventFeed = calendarService.getFeed(eventsURL, CalendarEventFeed.class);
        } catch (RedirectRequiredException e) {
            log.warning("Redirecting to new url: " + e.getRedirectLocation() + ": " + e.toString());
            calendarEventFeed = calendarService.getFeed(new URL(e.getRedirectLocation()), CalendarEventFeed.class);
        }

        return calendarEventFeed;
    }

    public void startBatch(String calId, BatchResultProcessor batchResultProcessor) throws MalformedURLException {
        this.batchUrl = new URL("http://www.google.com/calendar/feeds/" + calId + "/private/full/batch?alt=atom");
        this.batchFeed = new CalendarEventFeed();
        this.batchResultProcessor = batchResultProcessor;

        resetBatch();
    }

    private void resetBatch() {
        batchDelete = 0;
        batchInsert = 0;
        batchFeed.getEntries().clear();
    }


    public void addBatchEntry(CalendarEventEntry eventEntry, String batchId, BatchOperationType operation) throws Exception {
        if (batchId != null) BatchUtils.setBatchId(eventEntry, batchId);
        BatchUtils.setBatchOperationType(eventEntry, operation);

        if (operation == BatchOperationType.DELETE) batchDelete++;
        else if (operation == BatchOperationType.INSERT) batchInsert++;

        final List<CalendarEventEntry> entries = batchFeed.getEntries();
        entries.add(eventEntry);
        if (entries.size() == MAX_BATCH_SIZE) {
            sendBatch();

            resetBatch();
        }
    }

    public void sendBatch() throws Exception {
        if (batchFeed == null || batchFeed.getEntries().size() == 0) return;

        log.info("Deleting " + batchDelete + " existing events");
        log.info("Creating " + batchInsert + " new events");

        // Get the batch link URL and send the batch request there.
        calendarService.getRequestFactory().setHeader("If-Match","*");
        CalendarEventFeed batchResponse = calendarService.batch(batchUrl, batchFeed);

        // Ensure that all the operations were successful.
        String error = batchResultProcessor.processBatchResultEntry(batchResponse);

        if (error != null ) {
            throw new Exception("Batch failed. Last error: " + error);
        }
    }

    public CalendarFeed getUserCalendarsFeed() throws IOException, ServiceException {
        final URL feedUrl = new URL("http://www.google.com/calendar/feeds/default/allcalendars/full?alt=atom");
        final CalendarFeed calendarFeed = calendarService.getFeed(feedUrl, CalendarFeed.class);

        return calendarFeed;
    }


    public static interface BatchResultProcessor {
        String processBatchResultEntry(CalendarEventFeed batchResponse);
    }


    public static Document loadEvents(String calendarId) throws Exception {
        URL calendarUrl = getCalendarEventsURL(calendarId, false);
        log.info("Downloading calendar at " + calendarUrl);

        final InputStream inputStream = calendarUrl.openStream();

        final DocumentBuilder builder = XMLUtil.DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
        final Document document = builder.parse(inputStream);

        return document;
    }

    private static URL getCalendarEventsURL(String calId, boolean isPrivate) throws MalformedURLException {
        //start-min
        // a week ago
        //final long now = System.currentTimeMillis();
        //final int WEEK = 1000 * 60 * 60 * 24 * 7;
        //final long startMin = now - WEEK;
        //final long startMax = now + WEEK;
        return new URL("http://www.google.com/calendar/feeds/"
                + calId + "/"
                + (isPrivate ? "private" : "public")
                + "/full?alt=atom&max-results=1000");
                //+ "&start-min=" + XMLUtil.date2XmlDate(new Date(startMin), DatatypeConstants.DATE).toXMLFormat()
                //+ "&start-max=" + XMLUtil.date2XmlDate(new Date(startMax), DatatypeConstants.DATE).toXMLFormat());
    }


}
